%{ /*C++ declarations*/
#include <stdio.h>
#include <stdlib.h>
#include <string>
#include <vector>
#include <iostream>
#include "CodeGenContext.hpp"
#include "MTBlock.hpp"
MathematicaTranslator::MTBlock* programBlock;
	
const std::string bisonDebugMessageBeginning = "  <BISON DEBUG>: " ;

void bisonDebugMessage(const std::string& msg)
{
		std::cout<<bisonDebugMessageBeginning<<msg << std::endl;	
}

%}

/*bison declarations*/

%require "2.4.1"

/*start symbol is named "start"*/
%start start

/*switch them off in release*/
%debug
%error-verbose

%defines 
/*newer C++ skeleton file*/
%skeleton "lalr1.cc"
%name-prefix="MathematicaTranslator"
%define parser_class_name "Parser"

/* keep track of the current position within the input */
%locations
%initial-action
{
    // initialize the initial location object
    @$.begin.filename = @$.end.filename = &driver.streamname;
};

%parse-param { class MTDriver& driver }

%code requires {

#include "MTNode.hpp"

#include "MTExpression.hpp"
#include "MTBlock.hpp"
#include "MTStatement.hpp"

#include "MTDouble.hpp"
#include "MTInteger.hpp"
#include "MTFloat.hpp"
#include "MTIdentifier.hpp"
#include "MTList.hpp"
#include "MTMatrix.hpp"
#include "MTMethodCall.hpp"
#include "MTBinaryOperator.hpp"
#include "MTMatrixBinaryOperator.hpp"
#include "MTListBinaryOperator.hpp"
#include "MTUnaryOperator.hpp"
#include "MTParenExpression.hpp"

#include "MTVariableDefinition.hpp"
#include "MTVariableDeclaration.hpp"
#include "MTContainerVariableDefinition.hpp"
#include "MTFunctionDefinition.hpp"
#include "MTWhileStatement.hpp"
#include "MTForStatement.hpp"
#include "MTIfStatement.hpp"
#include "MTExpressionStatement.hpp"

#include "Params.h"
}

%union {
	MTNode* node;
	MTExpression* expr;
	MTIdentifier* id;
	MTVariableDefinition* var_def;
	MTContainerVariableDefinition* con_var_def;
	MTVariableDeclaration* var_decl;
	MTBlock* block;
	MTStatement* statement;
	MTList* list;

	ExpressionList* expr_vec;   
	Matrix* matrix_vec;

	VariableList*	var_vec;
	DeclaredVariableList*	decl_var_vec;

	int token;
	std::string* string;
}

%token <string> ID 
%token <string> DECINTEGER HEXINTEGER OCTINTEGER FLOATNUMBER DOUBLENUMBER ADD SUB MUL DIV EQ DEC INC DOT AND OR ISEQUAL ISNOTEQUAL GT GE LT LE
%token <string> KEYWORD

%token <token> ASSIGN
%token <token> BRACEOPEN BRACECLOSE PARENOPEN PARENCLOSE BRACKETOPEN BRACKETCLOSE
%token <token> END 0 "end of file"
%token <token> EOL "end of line"

/* Define the type of node our nonterminal symbols represent.
   The types refer to the %union declaration above. Ex: when
   we call an id (defined by union type id) we are really
   calling an (MTIdentifier*). It makes the compiler happy.
 */

%type <id> identifier
%type <string> comparison matrix_operation list_operation
%type <expr> variable expression list_expression matrix_expression container_type_expression primary term matrix_definition conditions condition 
%type <expr_vec> increment_statements list_elements function_actual_argument_list
%type <var_vec> for_init_list
%type <decl_var_vec> function_argument_list
%type <block> statements
%type <statement> control_statement statement definition function_statement
%type <var_def> variable_definition
%type <con_var_def> container_variable_definition
%type <var_decl> variable_declaration
%type <matrix_vec> matrix_rows
%type <list> list_definition

%right EQ
%left ADD SUB
%left MUL DIV DOT

%{
#include "MTDriver.hh"
#include "Scanner.h"

/* this "connects" the bison parser in the driver to the flex scanner class
 * object. it defines the yylex() function call to pull the next token from the
 * current lexer object of the driver context. */
#undef yylex
#define yylex driver.scanner->lex
%}

%%

start		:	/* empty */
| statements END	{ 
						#ifdef DEBUG
							bisonDebugMessage("START PROGRAM");
						#endif		
						programBlock = $1; 				
					}
;

statements	:	statement ';' statements	{ 
												#ifdef DEBUG
													bisonDebugMessage("STATEMENTS");
												#endif
											
												$3->statements.push_back( $1 ); 
												$$ = $3;					
											}
| statement	{ 
					#ifdef DEBUG
						bisonDebugMessage("SINGLE STATEMENT");
					#endif
					$$ = new MTBlock(); $$->statements.push_back( $1 ); 
			}
;

statement	:	expression	{ 
								#ifdef DEBUG
									bisonDebugMessage("CREATING EXPRESSION STATEMENT");
								#endif
								$$ = new MTExpressionStatement( *$1 ); 
							}
| control_statement	
| definition
| container_type_expression
; /* TODO add matrix_expression */

control_statement	:	'If'	BRACKETOPEN	conditions	','	statements	','	statements	BRACKETCLOSE	{ 
																												#ifdef DEBUG
																													bisonDebugMessage("CREATING 'IF' STATEMENT");
																												#endif
																												$$ = new MTIfStatement( *$3, *$5 , *$7 ); 
																										}
| 'While'	BRACKETOPEN	conditions ','	statements	BRACKETCLOSE	{ 
																			#ifdef DEBUG
																			bisonDebugMessage("CREATING 'WHILE' STATEMENT");
																			#endif
																			$$ = new MTWhileStatement( *$3, *$5 ); 
																	}
| 'For'	BRACKETOPEN	for_init_list 	','	conditions	','	increment_statements	',' statements	BRACKETCLOSE	{ 
																														#ifdef DEBUG
																															bisonDebugMessage("CREATING 'FOR' STATEMENT ");
																														#endif
																														$$ = new MTForStatement( *$3, *$5, *$7, *$9 ); 
																												}
;

for_init_list	:	variable_definition	{ 
											#ifdef DEBUG
												bisonDebugMessage("CREATING FOR INIT LIST");
											#endif
											$$ = new VariableList(); $$->push_back( $<var_def>1 ); 
										} 
| variable_definition ';' for_init_list	{	
											#ifdef DEBUG
												bisonDebugMessage("PUSHING VARIABLE DEFINITION TO 'FOR INIT LIST'");
											#endif
											$3->push_back( $<var_def>1 );	
											$$ = $3;
										}
;

increment_statements	:	expression	{ 
											#ifdef DEBUG
												bisonDebugMessage("CREATING EXPRESSION IN INCREMENT STATEMENT ");
											#endif
											$$ = new ExpressionList(); $$->push_back($1);
										}
| expression ';' increment_statements	{ 
											#ifdef DEBUG
												bisonDebugMessage("PUSHING EXPRESSION TO LIST IN INCREMENT STATEMENT");
											#endif
											$3->push_back($1); 
										}
;

definition		:	variable_definition
| container_variable_definition
| identifier	BRACKETOPEN	function_argument_list	BRACKETCLOSE	ASSIGN	function_statement	{ 
																										#ifdef DEBUG
																											bisonDebugMessage("CREATING FUNCTION DEFINITION");
																										#endif
																										$$ = new MTFunctionDefinition( *$1, *$3, *$6 ); 
																								}
;

function_statement : control_statement
| expression	{ 
					#ifdef DEBUG
						bisonDebugMessage("CREATING EXPRESSION IN FUNCTION STATEMENT");
					#endif
					$$ = new MTExpressionStatement( *$1 ); 
				}
;

variable_definition	:	identifier	EQ	expression							{ 
																				#ifdef DEBUG
																					bisonDebugMessage("CREATING VARIABLE DEFINITION");
																				#endif
																				$$ = new MTVariableDefinition( *$1, $3 ); 
																			}
;

variable_declaration : identifier											{ 
																				#ifdef DEBUG
																					bisonDebugMessage("CREATING VARIABLE DECLARATION");
																				#endif
																				$$ = new MTVariableDeclaration( *$1 ); 
																			}
;
container_variable_definition	:	identifier	EQ	container_type_expression	{
																					#ifdef DEBUG
																						bisonDebugMessage("CREATING CONTAINER VARIABLE DECLARATION");
																					#endif
																					$$ = new MTContainerVariableDefinition( *$1, $3 );
																				}
;

function_argument_list	:	variable_declaration '_' ',' function_argument_list	{ 
																					#ifdef DEBUG
																						bisonDebugMessage("PUSHING VARIABLE DECLARATION INTO FUNCTION ARGUMENT LIST");
																					#endif
																					
																					$4->push_back( $<var_decl>1 ); 
																					$$ = $4;
																				}
| variable_declaration '_'	{
								#ifdef DEBUG
									bisonDebugMessage("CREATING VARIABLELIST IN FUNCTION ARGUMENT LIST");
								#endif

								$$ = new DeclaredVariableList(); $$->push_back( $<var_decl>1 ); 
							}
;

function_actual_argument_list	:	expression	','	function_actual_argument_list	{
																						#ifdef DEBUG
																							bisonDebugMessage("PUSHING EXPRESSION TO 'ACTUAL ARGUMENT LIST' EXPRESSION LIST");
																						#endif
																						$3->push_back($1);
																						$$ = $3;
																					}
| expression				{
								#ifdef DEBUG
									bisonDebugMessage("CREATING 'ACTUAL ARGUMENT LIST' EXPRESSION LIST");
								#endif
								$$ = new ExpressionList(); $$->push_back($1);
							}
;

conditions	:	condition 
| conditions AND conditions	{
								#ifdef DEBUG
									bisonDebugMessage("CREATING BINARY OPERATOR 'AND'");
								#endif
								$$ = new MTBinaryOperator( *$1, *$2, *$3); 
							}
| conditions OR conditions  { 
								#ifdef DEBUG
									bisonDebugMessage("CREATING BINARY OPERATOR 'OR'");	
								#endif
								$$ = new MTBinaryOperator( *$1, *$2, *$3); 
							}
;

condition	:	expression comparison expression { 
													#ifdef DEBUG
														bisonDebugMessage("CREATING BINARY OPERATOR 'COMPARISON'");
													#endif
													$$ = new MTBinaryOperator( *$1, *$2, *$3 ); 
												 }
;

comparison	:	ISEQUAL | ISNOTEQUAL | GT | GE | LT | LE 
;

expression	:	term
| expression ADD term		{ 
									#ifdef DEBUG
										bisonDebugMessage("CREATING EXPRESSION 'ADD' TERM");
									#endif
									$$ = new MTBinaryOperator( *$1, *$2, *$3 ); 
							}
| expression SUB term		{ 
									#ifdef DEBUG
										bisonDebugMessage("CREATING EXPRESSION 'SUB' TERM");
									#endif
									$$ = new MTBinaryOperator( *$1, *$2, *$3 ); 
							}
;

term : primary
| term MUL primary			{ 
									#ifdef DEBUG
										bisonDebugMessage("CREATING TERM 'MUL' PRIMARY");
									#endif
									$$ = new MTBinaryOperator( *$1, *$2, *$3 ); 
							}	 
| term DIV primary			{ 
									#ifdef DEBUG
										bisonDebugMessage("CREATING TERM 'DIV' PRIMARY");
									#endif
									$$ = new MTBinaryOperator( *$1, *$2, *$3 ); 
							}	  
| term DOT primary			{ 
									#ifdef DEBUG
										bisonDebugMessage("CREATING TERM 'DOT' PRIMARY");
									#endif
									$$ = new MTBinaryOperator( *$1, *$2, *$3 ); 
							}	  
;

primary	:	variable { 
						#ifdef DEBUG
							bisonDebugMessage("PASSING ON VARIABLE IN PRIMARY");
						#endif
					 }
| identifier DEC			 {
								#ifdef DEBUG
									bisonDebugMessage("CREATING POST DECREMENT OPERATOR");
								#endif
								$$ = new MTUnaryOperator( *$1, *$2, POST );
							 }
| identifier INC			 {
								#ifdef DEBUG
									bisonDebugMessage("CREATING POST INCREMENT OPERATOR");
								#endif
								$$ = new MTUnaryOperator( *$1, *$2, POST );
							 }
| INC identifier			 {
								#ifdef DEBUG
									bisonDebugMessage("CREATING PRE INCREMENT OPERATOR");
								#endif
								$$ = new MTUnaryOperator( *$2, *$1, PRE );
							 }
| DEC identifier			 {
								#ifdef DEBUG
									bisonDebugMessage("CREATING PRE DECREMENT OPERATOR");
								#endif
								$$ = new MTUnaryOperator( *$2, *$1, PRE );
							 }
| identifier	BRACKETOPEN	function_actual_argument_list	BRACKETCLOSE		{
																					#ifdef DEBUG
																						bisonDebugMessage("CREATING METHOD CALL");
																					#endif
																					$$ = new MTMethodCall( *$1, *$3);
																				}
| identifier	{
					#ifdef DEBUG
						bisonDebugMessage("PASSING ON IDENTIFIER WITHIN EXPRESSION");
					#endif
					$$ = $1;
				}
| PARENOPEN expression PARENCLOSE	{
										#ifdef DEBUG
											bisonDebugMessage("CREATING EXPRESSION IN PARENTHESIS WITHIN EXPRESSION");
										#endif
										$$ = new MTParenExpression( *$2 );
									}
;

identifier	:	ID	{ 
						#ifdef DEBUG
							bisonDebugMessage("CREATING ID");
						#endif
						$$ = new MTIdentifier(*$1); 
						delete $1; 
					}
;

matrix_definition	:	BRACEOPEN	matrix_rows	BRACECLOSE	{
																#ifdef DEBUG
																	bisonDebugMessage("CREATING ID");
																#endif
																$$ = new MTMatrix( $2 ); 
															} 
;

matrix_rows	:	list_definition ',' matrix_rows { 
													#ifdef DEBUG
														bisonDebugMessage("PUSHING LIST DEFINITION INTO MATRIX ROWS");
													#endif
													$3->push_back($<list>1); 
													$$ = $3;
												} 
| list_definition ',' list_definition	{ 
											#ifdef DEBUG
												bisonDebugMessage("CREATING MATRIX WITH LIST DEFINITIONS");
											#endif
											$$ = new Matrix(); $$->push_back($<list>3); $$->push_back($<list>1); 
										}
;

list_definition	:	BRACEOPEN	list_elements	BRACECLOSE	{
																#ifdef DEBUG
																	bisonDebugMessage("CREATING NEW LIST");
																#endif 
																$$ = new MTList($2);
															}
;

list_elements	:	/* empty */	{ 
									#ifdef DEBUG
										bisonDebugMessage("CREATING EMPTY 'LIST ELEMENTS' EXPRESSION LIST");
									#endif
									$$ = new ExpressionList(); 
								}
| list_elements ',' expression	{ 
									#ifdef DEBUG
										bisonDebugMessage("PUSHING EXPRESSION INTO 'LIST ELEMENTS'");
									#endif
									$1->push_back($<expr>3); 
								}
| expression	{	
					#ifdef DEBUG
						bisonDebugMessage("CREATING EXPRESSION_LIST IN LIST");
					#endif
					$$ = new ExpressionList();	$$->push_back($<expr>1);	
				}
;

variable	:	DECINTEGER	{   
								#ifdef DEBUG
									bisonDebugMessage("CREATING INTEGER NUMBER: ");
								#endif

								$$ = new MTInteger(atol($1->c_str()));
								delete $1; 
							}
| HEXINTEGER	
| OCTINTEGER	
| FLOATNUMBER	{ 
					#ifdef DEBUG
						bisonDebugMessage("CREATING FLOAT NUMBER");
					#endif
					$$ = new MTFloat(static_cast<float>(atof( $1->c_str() ) ) );
					delete $1; 
				}
| DOUBLENUMBER  {
					#ifdef DEBUG
						bisonDebugMessage("CREATING DOUBLE NUMBER");
					#endif
					$$ = new MTDouble(atof($1->c_str()));
					delete $1; 
				}
;

container_type_expression	:	matrix_expression
| list_expression
;

matrix_expression	:	matrix_definition	matrix_operation	matrix_expression	{
																						#ifdef DEBUG
																							bisonDebugMessage("CREATING MATRIX OPERATION");
																						#endif
																						$$ = new MTMatrixBinaryOperator( *$1, *$2, *$3 );
																					}
| matrix_definition
;

list_expression	:	list_definition	list_operation	list_expression					{
																						#ifdef DEBUG
																							bisonDebugMessage("CREATING LIST OPERATION");
																						#endif
																						$$ = new MTListBinaryOperator( *$1, *$2, *$3 );
																					}
| list_definition
;

matrix_operation	:	ADD
| MUL
;

list_operation	:	ADD
| MUL
| DOT
;

%%  /* additional code */


void MathematicaTranslator::Parser::error(const MathematicaTranslator::Parser::location_type &l, const std::string &m)
{
	driver.error(l,m);
}
